﻿namespace StockAnalyzer.HaoStock
{
    public class AccountModel
    {
        /************* 模型参数 *****************/

        public StockData data = new StockData();

        public List<StockTradePlan> plans = new List<StockTradePlan>();
        public List<TradeRecord> records = new List<TradeRecord>();
        public List<StockDayKLine> kdata = new List<StockDayKLine>();

        public string stock_code = "";

        public string stock_name = "";

        /// <summary>
        /// 当前资金数量。
        /// </summary>
        public double money_value = 1_000_000;

        /// <summary>
        /// 初始资金数量。
        /// </summary>
        public double initial_value = 1_000_000;

        /// <summary>
        /// 目标买入价上限，默认值为0，若为0，则会读取上1年的参数，再乘以1.2。
        /// </summary>
        public double in_price = 0;

        /// <summary>
        /// 目标卖出价上限，默认值为0，若为0，则会读取上1年的参数，再乘以1.2。
        /// </summary>
        public double out_price = 0;
        // 12.00 --> 2,747,613.400
        // 12.08 --> 3,023,024.728

        /// <summary>
        /// 收益率。
        /// </summary>
        public double interest_rate = 0.25;

        /// <summary>
        /// 目标年份，默认是2024年。
        /// </summary>
        public int target_year = 2024;

        /************* 配置项 *****************/
        public bool print_details = false;
        public bool print_history = false;
        public bool print_header = false;




        public AccountModel() { }

        public AccountModel(double money)
        {
            initial_value = money;
            money_value = money;
        }

        /// <summary>
        /// 返回当前持仓水平。
        /// </summary>
        /// <returns></returns>
        public double GetMoneyLevel()
        {
            return money_value / initial_value;
        }


        /// <summary>
        /// 返回所有股票的总价值。
        /// </summary>
        /// <returns></returns>
        public double GetAllStockValue()
        {
            double total = 0;
            foreach (var record in records)
                if (record.state == OperationState.Bought)
                    total += record.buy_price * record.buy_amount;
            return total;
        }



        /// <summary>
        /// 返回持有股票数量。
        /// </summary>
        /// <returns></returns>
        public double GetTotalStockAmount()
        {
            double total_amount = 0;
            foreach (var record in records)
                if (record.state == OperationState.Bought)
                    total_amount += record.buy_amount;

            return total_amount;
        }


        public double GetCostPrice()
        {
            if (GetTotalStockAmount() == 0)
                return 0;
            return GetAllStockValue() / GetTotalStockAmount();
        }

        public double GetMidPrice(List<StockDayKLine> infos, int year)
        {
            double all_mid_price = 0;
            int count0 = 0;
            foreach (StockDayKLine info in infos)
            {
                if (info.date.Year == year)
                {
                    all_mid_price += info.GetMid();
                    count0++;
                }
            }
            return count0 > 0 ? all_mid_price / count0 : 0;
        }




        public string Run()
        {
            return Run(kdata);
        }

        public string Run(string file)
        {
            return Run(StockDayKLine.read_csv(file));
        }

        public string Run(List<StockDayKLine> infos)
        {
            kdata = infos;
            var mid_last_year = GetMidPrice(infos, target_year - 1);
            var mid_target_year = GetMidPrice(infos, target_year);

            if (in_price == 0 || out_price == 0)
            {
                in_price = mid_last_year * 1.1;
                out_price = in_price * 1.1;
            }



            for (int i = 0; i <= 20; i++)
            {
                StockTradePlan plan = new StockTradePlan();
                plan.target_buy_price = (1 - i * 0.02) * in_price;
                plan.target_sell_price = plan.target_buy_price * (1.0 + interest_rate);
                plan.target_sell_price = plan.target_sell_price > out_price ? out_price : plan.target_sell_price;
                plan.target_level = 1 - 0.05 * i;
                plans.Add(plan);
            }




            records.Clear();
            StockDayKLine last_info = infos[0];
            foreach (var info in infos)
            {
                if (info.date.Year != target_year)
                    continue;
                last_info = info;

                if (print_details)
                    // 2024-04-15  total: 1,133,100   price/cost: 11.14/ 0.00,  l/h/c: 10.74  11.80  11.14
                    Console.WriteLine($"{info.date:yyyy-MM-dd} l/h/c: {info.low,6:N2}  {info.high,6:N2}  {info.close,6:N2}, "
                        + $"cost: {GetCostPrice(),5:N2}, amount: {GetTotalStockAmount() / 10000,6:N2} => {GetTotalValue(info.close),10:N0}");

                // 买入操作
                foreach (var plan in plans)
                {
                    if (info.low >= plan.target_buy_price)
                        continue;

                    // 计算买入量
                    var current_total_value = GetTotalValue(info.close);
                    var current_money_percent = money_value / current_total_value;

                    var new_investment = (current_money_percent - plan.target_level) * current_total_value;
                    if (new_investment <= 0)
                        continue;

                    int amount0 = (int)(new_investment / plan.target_buy_price);
                    if (amount0 < 100)
                        continue;

                    amount0 = amount0 / 100 * 100;

                    // 买入并更新买入信息
                    new_investment = amount0 * plan.target_buy_price;
                    new_investment = new_investment > money_value ? money_value : new_investment;

                    TradeRecord trade = new TradeRecord();
                    trade.buy(info.date, plan.target_buy_price, new_investment / plan.target_buy_price);
                    trade.sell_price = plan.target_sell_price;
                    trade.sell_amount = trade.buy_amount;
                    records.Add(trade);

                    money_value -= new_investment;
                    if (print_details)
                    {
                        Console.WriteLine($"  B: {trade.buy_price,5:N2} x {trade.buy_amount,5:0} = {new_investment:0} "
                            + $" STATS: {GetCostPrice(),5:N2} x {GetTotalStockAmount(),5:0} = "
                            + $"{GetCostPrice() * GetTotalStockAmount(),7:N0}, {money_value / 10000,6:N2}W");
                    }
                }



                // 卖出操作
                foreach (var trade in records)
                {
                    if (trade.state != OperationState.Bought || trade.buy_date == info.date)
                        continue;

                    if (info.low < trade.sell_price && trade.sell_price < info.high)
                    {
                        trade.state = OperationState.Sold;
                        money_value += trade.sell(info.date, trade.sell_price);

                        if (print_details)
                            Console.WriteLine($"  S: {records[^1]}");
                    }
                }


            }


            int id = 1;
            if (print_history)
            {
                Console.WriteLine($"--------------------  Action Plans ------------------------");
                foreach (var plan in plans)
                    Console.WriteLine(plan);

                Console.WriteLine($"--------------------  History Records ------------------------");
                foreach (var trade in records.OrderBy(t => t.buy_date))
                    Console.WriteLine($"{id++,3}. {trade}");
                Console.WriteLine();
            }

            double stock_value = GetStockValue(last_info.close);
            double total_value = money_value + stock_value;
            double stock_amount = GetTotalStockAmount();

            if (print_header)
                Console.WriteLine($"    code        year       money           stock          total          avg2023     avg2024      In/Out");
            //                   sh.600749      2023    356,288.31      830,144.00    1,186,432.31       12.44       11.20    12.05/13.01

            var stats = StockData.Statictics(infos, target_year);
            string[] strs = new string[12];
            strs[0] = $"{stock_name,9}";
            strs[1] = $"{target_year,4}";
            strs[2] = $"{money_value,10:N0}";
            strs[3] = $"{stock_value,10:N0}";
            strs[4] = $"{total_value,10:N0}";
            strs[5] = $"{mid_last_year,6:N2}";
            strs[6] = $"{mid_target_year,6:N2}";
            strs[7] = $"{in_price,6:N2}";
            strs[8] = $"{out_price,6:N2}";
            strs[9] = $"{stats[1],6:0.00}";
            strs[10] = $"{stats[2],6:0.00}";
            strs[11] = $"{stats[3],6:0.0%}";
            string out_str = string.Join("   ", strs);
            Console.WriteLine(out_str);

            return out_str;
            // Console.WriteLine($"{stock_name,10}{target_year,10}{money_value,16:N2}{stock_value,16:N2}{total_value,16:N2}{mid_last_year,12:N2}{mid_target_year,12:N2}   {in_price,6:N2} {out_price:N2} {stats[4], 6:0.0%}");
        }

        public double GetTotalValue(double current_price)
        {
            double stock_value = GetStockValue(current_price);
            double stock_amount = GetTotalStockAmount();
            double total_cost = GetAllStockValue();
            double total_value = money_value + stock_value;
            return total_value;
        }

        private double GetStockValue(double current_price)
        {
            double total = 0;
            foreach (var trade in records)
            {
                if (trade.state == OperationState.Bought)
                    total += trade.buy_amount * current_price;
            }

            return total;
        }
    }


}
